﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;

namespace TestStudent
{
    [TestClass]
    public class ICourseExamTest
    {
        [TestMethod]
        public void AddExamResultTestCorrect()
        {
            ICourseExam target = CreateICourseExam();
            Student student = new Student("Batman", 14222);
            string name = "Cliff Jumping";
            int grade = 6;
            target.AddExamResult(student, name, grade);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void AddExamResultTestEmptyExamName()
        {
            ICourseExam target = CreateICourseExamWithoutExamName();
            Student student = new Student("Pesho", 14242);
            string name = "";
            int grade = 2;
            target.AddExamResult(student, name, grade);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void AddExamResultTestIncorrectGrade()
        {
            ICourseExam target = CreateICourseExamWihWrongGrade();
            Student student = new Student("Pesho", 24148);
            string name = "Beer Drinking";
            int grade = 7;
            target.AddExamResult(student, name, grade);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AddExamResultTestNullStudent()
        {
            ICourseExam target = CreateICourseExamWithoutStudent();
            Student student = null;
            string name = "Math";
            int grade = 2;
            target.AddExamResult(student, name, grade);
        }

        [TestMethod]
        public void GetExamResultsTestCorrect()
        {
            ICourseExam target = CreateICourseExamForGettingResults();
            Student student = new Student("Superman", 11222); 
            IList<int> expected = new List<int>() { 6, 3, 2 };
            IList<int> actual = target.GetExamResults(student);
            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(actual[i], expected[i]);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetExamResultsTestWithNullStudent()
        {
            ICourseExam target = CreateICourseExamForGettingResultsWithNullReference();
            Student student = null;
            target.GetExamResults(student);
        }

        internal virtual ICourseExam CreateICourseExam()
        {
            var targetMock = new Mock<ICourseExam>();
            targetMock.Setup(x => x.AddExamResult(new Student("Batman", 14222), "Cliff Jumping", 6));
            return targetMock.Object;
        }

        internal virtual ICourseExam CreateICourseExamForGettingResults()
        {
            var targetMock = new Mock<ICourseExam>();
            IList<int> grades = new List<int>() { 6, 3, 2 };
            targetMock.Setup(x => x.GetExamResults(new Student("Superman", 11222))).Returns(grades);
            return targetMock.Object;
        }

        internal virtual ICourseExam CreateICourseExamForGettingResultsWithNullReference()
        {
            var targetMock = new Mock<ICourseExam>();
            targetMock.Setup(x => x.GetExamResults(null)).Throws(
                new ArgumentNullException("Student must be initialized"));
            return targetMock.Object;
        }

        internal virtual ICourseExam CreateICourseExamWihWrongGrade()
        {
            var targetMock = new Mock<ICourseExam>();
            targetMock.Setup(x => x.AddExamResult(It.IsAny<Student>(), It.IsAny<string>(), 7)).Throws(
                new ArgumentOutOfRangeException("Grade must be between 2 and 6"));
            return targetMock.Object;
        }

        internal virtual ICourseExam CreateICourseExamWithoutExamName()
        {
            var targetMock = new Mock<ICourseExam>();
            targetMock.Setup(x => x.AddExamResult(new Student("Pesho", 14242), string.Empty, 2))
            .Throws(new ArgumentException("Exam must be initialized"));
            return targetMock.Object;
        }

        internal virtual ICourseExam CreateICourseExamWithoutStudent()
        {
            var targetMock = new Mock<ICourseExam>();
            targetMock.Setup(x => x.AddExamResult(null, "Math", 2)).Throws(
                new ArgumentNullException("Student must be initialized"));
            return targetMock.Object;
        }
    }
}